#include "../include/CodeGenerator.h"

fstream fa;//虚拟机代码 NOLINT
int locations = 0;
int bl;//blockList
int fSize;
bool noUnaryExp = false;
stack<past> needJPC;

void BlockList() {
    if (whichFunc) {
        for (int i = 0; i < fNum; i++) {
            if (strcmp(func[i].name, whichFunc) == 0) {
                if (bl == func[i].blockList) { code.emplace_back(fct::RET, 0, 0); }
            }
        }
    }
}

void CodeGenerator::entry() {
    code[0].a = int(code.size());
    for (int i = 0; i < fNum; i++) {
        code[i + 1].a = func[i].location;
    }
    code.emplace_back(fct::INT, 0, gvNum + 2);
    for (int i = 0; i < fNum; i++) {
        if (strcmp(func[i].name, "main") == 0) {
            code.emplace_back(fct::CAL, 0, func[i].location);
        }
    }
    code.emplace_back(fct::RET, 0, 0);
}

void CodeGenerator::visit(CompUnit *) {
    for (int i = 0; i <= fNum; i++) {
        code.emplace_back(fct::JMP, 0, 0);
    }
}

void CodeGenerator::visit(FuncDef *) {
    bl = 0;
    func[locations++].location = int(code.size());
    code.emplace_back(fct::INT, 0, func[fSize++].size);
    whichFunc = node->left->left->next->strValue;
}

void CodeGenerator::visit(Assign_Stmt *) {
    queue<past> q;
    stack<past> p;
    if (node->ifJPC) {
        code[needJPC.top()->loc].a = code.size();
        needJPC.pop();
    }
    if (node->blInIf) bl = node->blInIf;
    else if (node->blInWhile) bl = node->blInWhile;
    else bl++;
    if (strcmp(node->right->nodeType, "UnaryExp") == 0) {
        noUnaryExp = true;
        if (node->right->left->ivalue == '-') {
            code.emplace_back(fct::LIT, 0, -node->right->left->right->ivalue);
            if (strcmp(node->left->nodeType, "parameter") == 0) {
                for (int i = 0; i < gvNum; i++) {
                    if (strcmp(node->left->strValue, gv[i].name) == 0) {
                        for (int j = 0; j < fNum; j++) {
                            if (strcmp(whichFunc, func[j].name) == 0) {
                                code.emplace_back(fct::STO, func[j].level, gv[i].adr);
                            }
                        }
                    }
                }
            }
            if (strcmp(node->left->nodeType, "parameter") == 0) {
                for (int i = 0; i < lvNum; i++) {
                    if (strcmp(node->left->strValue, lv[i].name) == 0) {
                        code.emplace_back(fct::STO, 0, gv[i].adr);
                    }
                }
            }
        }
        return;
    }
    if (node != nullptr)
        q.push(node);   //根节点进队列
    while (!q.empty()) {
        past expr = q.front();
        p.push(expr);
        if (expr->left != nullptr)   //如果有左孩子，入队
            q.push(expr->left);
        if (expr->right != nullptr)   //如果有右孩子，入队
            q.push(expr->right);
        q.pop();
    }
    while (!p.empty()) {
        past p1 = p.top();
        p.pop();
        if (!p.empty()) {
            past p2 = p.top();
            if (strcmp(p1->nodeType, "expr") == 0) {//p1 is expr
                switch (p1->ivalue) {
                    case '+':
                        code.emplace_back(fct::ADD, 0, 0);
                        break;
                    case '-':
                        code.emplace_back(fct::SUB, 0, 0);
                        break;
                    case '*':
                        code.emplace_back(fct::MUL, 0, 0);
                        break;
                    case '/':
                        code.emplace_back(fct::DIV, 0, 0);
                        break;
                }
                if (strcmp(p2->nodeType, "parameter") == 0) {
                    for (int i = 0; i < gvNum; i++) {
                        if (strcmp(p2->strValue, gv[i].name) == 0) {
                            for (int j = 0; j < fNum; j++) {
                                if (strcmp(whichFunc, func[j].name) == 0) {
                                    if (p.size() == 2)
                                        code.emplace_back(fct::STO, func[j].level, gv[i].adr);
                                    else
                                        code.emplace_back(fct::LOD, func[j].level, gv[i].adr);
                                }
                            }
                        }
                    }
                }
                if (strcmp(p2->nodeType, "parameter") == 0) {
                    for (int i = 0; i < lvNum; i++) {
                        if (strcmp(p2->strValue, lv[i].name) == 0) {
                            if (p.size() == 2)
                                code.emplace_back(fct::STO, 0, gv[i].adr);
                            else
                                code.emplace_back(fct::LOD, 0, gv[i].adr);
                        }
                    }
                }
                if (strcmp(p2->nodeType, "intValue") == 0) {
                    code.emplace_back(fct::LIT, 0, p2->ivalue);
                }
            } else if (strcmp(p2->nodeType, "expr") == 0) {//p2 is expr
                switch (p2->ivalue) {
                    case '+':
                        code.emplace_back(fct::ADD, 0, 0);
                        break;
                    case '-':
                        code.emplace_back(fct::SUB, 0, 0);
                        break;
                    case '*':
                        code.emplace_back(fct::MUL, 0, 0);
                        break;
                    case '/':
                        code.emplace_back(fct::DIV, 0, 0);
                        break;
                }
                if (strcmp(p1->nodeType, "parameter") == 0) {
                    for (int i = 0; i < gvNum; i++) {
                        if (strcmp(p1->strValue, gv[i].name) == 0) {
                            for (int j = 0; j < fNum; j++) {
                                if (strcmp(whichFunc, func[j].name) == 0) {
                                    code.emplace_back(fct::LOD, func[j].level, gv[i].adr);
                                }
                            }
                        }
                    }
                }
                if (strcmp(p1->nodeType, "parameter") == 0) {
                    for (int i = 0; i < lvNum; i++) {
                        if (strcmp(p1->strValue, lv[i].name) == 0) {
                            code.emplace_back(fct::LOD, 0, gv[i].adr);
                        }
                    }
                }
                if (strcmp(p1->nodeType, "intValue") == 0) {
                    code.emplace_back(fct::LIT, 0, p1->ivalue);
                }
            } else {//have no expr
                if (p.size() == 2) {
                    if (strcmp(p1->nodeType, "parameter") == 0) {
                        for (int i = 0; i < gvNum; i++) {
                            if (strcmp(p1->strValue, gv[i].name) == 0) {
                                for (int j = 0; j < fNum; j++) {
                                    if (strcmp(whichFunc, func[j].name) == 0) {
                                        code.emplace_back(fct::LOD, func[j].level, gv[i].adr);
                                    }
                                }
                            }
                        }
                    }
                    if (strcmp(p1->nodeType, "parameter") == 0) {
                        for (int i = 0; i < lvNum; i++) {
                            if (strcmp(p1->strValue, lv[i].name) == 0) {
                                code.emplace_back(fct::LOD, 0, gv[i].adr);
                            }
                        }
                    }
                    if (strcmp(p1->nodeType, "intValue") == 0) {
                        code.emplace_back(fct::LIT, 0, p1->ivalue);
                    }
                    if (strcmp(p2->nodeType, "parameter") == 0) {
                        for (int i = 0; i < gvNum; i++) {
                            if (strcmp(p2->strValue, gv[i].name) == 0) {
                                for (int j = 0; j < fNum; j++) {
                                    if (strcmp(whichFunc, func[j].name) == 0) {
                                        code.emplace_back(fct::STO, func[j].level, gv[i].adr);
                                    }
                                }
                            }
                        }
                    }
                    if (strcmp(p2->nodeType, "parameter") == 0) {
                        for (int i = 0; i < lvNum; i++) {
                            if (strcmp(p2->strValue, lv[i].name) == 0) {
                                code.emplace_back(fct::STO, 0, lv[i].adr);
                            }
                        }
                    }
                    if (strcmp(p2->nodeType, "intValue") == 0) {
                        code.emplace_back(fct::LIT, 0, p2->ivalue);
                    }
                } else {
                    if (strcmp(p2->nodeType, "parameter") == 0) {
                        for (int i = 0; i < gvNum; i++) {
                            if (strcmp(p2->strValue, gv[i].name) == 0) {
                                for (int j = 0; j < fNum; j++) {
                                    if (strcmp(whichFunc, func[j].name) == 0) {
                                        code.emplace_back(fct::LOD, func[j].level, gv[i].adr);
                                    }
                                }
                            }
                        }
                    }
                    if (strcmp(p2->nodeType, "parameter") == 0) {
                        for (int i = 0; i < lvNum; i++) {
                            if (strcmp(p2->strValue, lv[i].name) == 0) {
                                code.emplace_back(fct::STO, 0, lv[i].adr);
                            }
                        }
                    }
                    if (strcmp(p2->nodeType, "intValue") == 0) {
                        code.emplace_back(fct::LIT, 0, p2->ivalue);
                    }
                    if (strcmp(p1->nodeType, "parameter") == 0) {
                        for (int i = 0; i < gvNum; i++) {
                            if (strcmp(p1->strValue, gv[i].name) == 0) {
                                for (int j = 0; j < fNum; j++) {
                                    if (strcmp(whichFunc, func[j].name) == 0) {
                                        code.emplace_back(fct::LOD, func[j].level, gv[i].adr);
                                    }
                                }
                            }
                        }
                    }
                    if (strcmp(p1->nodeType, "parameter") == 0) {
                        for (int i = 0; i < lvNum; i++) {
                            if (strcmp(p1->strValue, lv[i].name) == 0) {
                                code.emplace_back(fct::LOD, 0, gv[i].adr);
                            }
                        }
                    }
                    if (strcmp(p1->nodeType, "intValue") == 0) {
                        code.emplace_back(fct::LIT, 0, p1->ivalue);
                    }
                }
            }
        } else if (p.empty()) {
            if (node->whileJPC) {
                code.emplace_back(fct::JMP, 0, needJPC.top()->loc);
                code[needJPC.top()->loc + 1].a = code.size();
                needJPC.pop();
                BlockList();
                return;
            } else return;
        }
        p.pop();
    }
}

void CodeGenerator::visit(Return_Stmt *) {
    bl++;
    if (node->ifJPC) {
        code[needJPC.top()->loc].a = code.size();
        needJPC.pop();
    }
    if (!node->left)
        code.emplace_back(fct::RET, 0, 0);
    else if (strcmp(node->left->nodeType, "intValue") == 0) {
        code.emplace_back(fct::LIT, 0, node->left->ivalue);
        code.emplace_back(fct::RET, 0, 0);
    } else if (strcmp(node->left->nodeType, "parameter") == 0) {
        for (int i = 0; i < gvNum; i++) {
            if (strcmp(node->left->strValue, gv[i].name) == 0) {
                for (int j = 0; j < fNum; j++) {
                    if (strcmp(whichFunc, func[j].name) == 0) {
                        code.emplace_back(fct::LOD, func[j].level, gv[i].adr);
                        code.emplace_back(fct::RET, 0, 0);
                    }
                }
            }
        }
        for (int i = 0; i < lvNum; i++) {
            if (strcmp(node->left->strValue, lv[i].name) == 0) {
                code.emplace_back(fct::LOD, 0, lv[i].adr);
                code.emplace_back(fct::RET, 0, 0);
            }
        }
    } else if (strcmp(node->left->nodeType, "expr") == 0) {

    } else if (strcmp(node->left->nodeType, "UnaryExp") == 0) {
        noUnaryExp = true;
        if (node->left->left->ivalue == '-') {
            code.emplace_back(fct::LIT, 0, -node->left->left->right->ivalue);
            code.emplace_back(fct::RET, 0, 0);
        }
    }
}

void CodeGenerator::visit(UnaryExp *) {
    if (noUnaryExp) {
        noUnaryExp = false;
        return;
    }
    if (node->ifJPC) {
        code[needJPC.top()->loc].a = code.size();
        needJPC.pop();
    }
    if (node->blInIf) bl = node->blInIf;
    else if (node->blInWhile) bl = node->blInWhile;
    else bl++;

    for (int i = 0; i < fNum; i++) {
        if (strcmp(node->left->strValue, func[i].name) == 0) {
            code.emplace_back(fct::CAL, 0, func[i].location);
        }
    }
    if (strcmp(node->left->strValue, "scanf") == 0) {
        for (int i = 0; i < gvNum; i++) {
            if (strcmp(node->right->left->strValue, gv[i].name) == 0) {
                for (int j = 0; j < fNum; j++) {
                    if (strcmp(whichFunc, func[j].name) == 0) {
                        code.emplace_back(fct::RED, 0, 0);
                        code.emplace_back(fct::STO, func[j].level, gv[i].adr);
                    }
                }
            }
        }
        for (int i = 0; i < lvNum; i++) {
            if (strcmp(node->right->left->strValue, lv[i].name) == 0) {
                code.emplace_back(fct::RED, 0, 0);
                code.emplace_back(fct::LOD, 0, lv[i].adr);
            }
        }
    } else if (strcmp(node->left->strValue, "printf") == 0) {
        if (strcmp(node->right->left->nodeType, "UnaryExp") == 0) {
            noUnaryExp = true;
            for (int i = 0; i < fNum; i++) {
                if (strcmp(node->right->left->left->strValue, func[i].name) == 0) {
                    code.emplace_back(fct::CAL, 0, func[i].location);
                    code.emplace_back(fct::WRT, 0, 0);
                }
            }
        } else if (strcmp(node->right->left->nodeType, "expr") == 0) {

        } else {
            for (int i = 0; i < gvNum; i++) {
                if (strcmp(node->right->left->strValue, gv[i].name) == 0) {
                    for (int j = 0; j < fNum; j++) {
                        if (strcmp(whichFunc, func[j].name) == 0) {
                            code.emplace_back(fct::LOD, func[j].level, gv[i].adr);
                        }
                    }
                }
            }
            for (int i = 0; i < lvNum; i++) {
                if (strcmp(node->right->left->strValue, lv[i].name) == 0) {
                    code.emplace_back(fct::LOD, 0, lv[i].adr);

                }
            }
            code.emplace_back(fct::WRT, 0, 0);

        }
    }
    BlockList();
}

void CodeGenerator::visit(IfElse_Stmt *) {
    queue<past> q;
    stack<past> p;
    bl++;
    if (node != nullptr)
        q.push(node);   //根节点进队列
    while (!q.empty()) {
        past expr = q.front();
        p.push(expr);
        if (expr->left != nullptr)   //如果有左孩子，入队
            q.push(expr->left);
        if (expr->right != nullptr && strcmp(expr->right->nodeType, "If_Else") != 0)   //如果有右孩子，入队
            q.push(expr->right);
        q.pop();
    }
    while (!p.empty()) {
        past p1 = p.top();
        p.pop();
        if (!p.empty()) {
            past p2 = p.top();
            if (strcmp(p1->nodeType, "expr") == 0) {//p1 is expr
                switch (p1->ivalue) {
                    case '+':
                        code.emplace_back(fct::ADD, 0, 0);
                        break;
                    case '-':
                        code.emplace_back(fct::SUB, 0, 0);
                        break;
                    case '*':
                        code.emplace_back(fct::MUL, 0, 0);
                        break;
                    case '/':
                        code.emplace_back(fct::DIV, 0, 0);
                        break;
                }
                if (strcmp(p2->nodeType, "parameter") == 0) {
                    for (int i = 0; i < gvNum; i++) {
                        if (strcmp(p2->strValue, gv[i].name) == 0) {
                            for (int j = 0; j < fNum; j++) {
                                if (strcmp(whichFunc, func[j].name) == 0) {
                                    if (p.size() == 2)
                                        code.emplace_back(fct::STO, func[j].level, gv[i].adr);
                                    else
                                        code.emplace_back(fct::LOD, func[j].level, gv[i].adr);
                                }
                            }
                        }
                    }
                }
                if (strcmp(p2->nodeType, "parameter") == 0) {
                    for (int i = 0; i < lvNum; i++) {
                        if (strcmp(p2->strValue, lv[i].name) == 0) {
                            if (p.size() == 2)
                                code.emplace_back(fct::STO, 0, gv[i].adr);
                            else
                                code.emplace_back(fct::LOD, 0, gv[i].adr);
                        }
                    }
                }
                if (strcmp(p2->nodeType, "intValue") == 0) {
                    code.emplace_back(fct::LIT, 0, p2->ivalue);
                }
                if (strcmp(p2->nodeType, "IfElse_Stmt") == 0) {
                    p2->loc = code.size();
                    code.emplace_back(fct::JPC, 0, 0);
                    needJPC.push(p2);
                }
            } else if (strcmp(p2->nodeType, "expr") == 0) {//p2 is expr
                switch (p2->ivalue) {
                    case '+':
                        code.emplace_back(fct::ADD, 0, 0);
                        break;
                    case '-':
                        code.emplace_back(fct::SUB, 0, 0);
                        break;
                    case '*':
                        code.emplace_back(fct::MUL, 0, 0);
                        break;
                    case '/':
                        code.emplace_back(fct::DIV, 0, 0);
                        break;
                }
                if (strcmp(p1->nodeType, "parameter") == 0) {
                    for (int i = 0; i < gvNum; i++) {
                        if (strcmp(p1->strValue, gv[i].name) == 0) {
                            for (int j = 0; j < fNum; j++) {
                                if (strcmp(whichFunc, func[j].name) == 0) {
                                    code.emplace_back(fct::LOD, func[j].level, gv[i].adr);
                                }
                            }
                        }
                    }
                }
                if (strcmp(p1->nodeType, "parameter") == 0) {
                    for (int i = 0; i < lvNum; i++) {
                        if (strcmp(p1->strValue, lv[i].name) == 0) {
                            code.emplace_back(fct::LOD, 0, gv[i].adr);
                        }
                    }
                }
                if (strcmp(p1->nodeType, "intValue") == 0) {
                    code.emplace_back(fct::LIT, 0, p1->ivalue);
                }
            } else {//have no expr
                if (strcmp(p2->nodeType, "parameter") == 0) {
                    for (int i = 0; i < gvNum; i++) {
                        if (strcmp(p2->strValue, gv[i].name) == 0) {
                            for (int j = 0; j < fNum; j++) {
                                if (strcmp(whichFunc, func[j].name) == 0) {
                                    code.emplace_back(fct::LOD, func[j].level, gv[i].adr);
                                }
                            }
                        }
                    }
                }
                if (strcmp(p2->nodeType, "parameter") == 0) {
                    for (int i = 0; i < lvNum; i++) {
                        if (strcmp(p2->strValue, lv[i].name) == 0) {
                            code.emplace_back(fct::STO, 0, lv[i].adr);
                        }
                    }
                }
                if (strcmp(p2->nodeType, "intValue") == 0) {
                    code.emplace_back(fct::LIT, 0, p2->ivalue);
                }
                if (strcmp(p1->nodeType, "parameter") == 0) {
                    for (int i = 0; i < gvNum; i++) {
                        if (strcmp(p1->strValue, gv[i].name) == 0) {
                            for (int j = 0; j < fNum; j++) {
                                if (strcmp(whichFunc, func[j].name) == 0) {
                                    code.emplace_back(fct::LOD, func[j].level, gv[i].adr);
                                }
                            }
                        }
                    }
                }
                if (strcmp(p1->nodeType, "parameter") == 0) {
                    for (int i = 0; i < lvNum; i++) {
                        if (strcmp(p1->strValue, lv[i].name) == 0) {
                            code.emplace_back(fct::LOD, 0, gv[i].adr);
                        }
                    }
                }
                if (strcmp(p1->nodeType, "intValue") == 0) {
                    code.emplace_back(fct::LIT, 0, p1->ivalue);
                }
                if (strcmp(p2->nodeType, "IfElse_Stmt") == 0) {
                    p2->loc = code.size();
                    code.emplace_back(fct::JPC, 0, 0);
                    needJPC.push(p2);
                }
            }
        }
        p.pop();
    }
}

void CodeGenerator::visit(Decl *) {
    bl++;
    if (node->ifJPC) {
        code[needJPC.top()->loc].a = code.size();
        needJPC.pop();
    }
    BlockList();
}

void CodeGenerator::visit(If_Else *) {
    node->left->next->ifJPC = true;
    node->blInIf = bl;
    node->left->blInIf = bl;
    node->left->next->blInIf = bl;
}

void CodeGenerator::visit(While_Stmt *) {
    bl++;
    node->right->blInWhile = bl;
    for (int i = 0; i < fNum; i++) {
        if (strcmp(node->left->left->strValue, func[i].name) == 0) {
            node->loc = code.size();
            code.emplace_back(fct::CAL, 0, func[i].location);
            code.emplace_back(fct::JPC, 0, 0);
            needJPC.push(node);
            noUnaryExp = true;
            node->right->whileJPC = true;
        }
    }
}

void CodeGenerator::showCode() {
    fa.open("fa.txt", ios::out);
    for (auto &i: code) {
        cout << fctToStr(i.f) << " " << i.l << " " << i.a << endl;
        gen(i.f, i.l, i.a);
    }
    fa.close();
}
